import { forEachValue } from "./utils";

export let Vue;

// 获取当前模块最新的state 【a，a/aa】
function getState(store, path) {
  return path.reduce((start, current) => {
    return start[current]
  }, store.state)
}

// 初始化处理模块数据
export function installModule(store, rootState, path, rootModule) {
  if (path.length > 0) {
    // 只有是子模块的时候才需要将子模块的状态定义在根模块上面
    // [a]、[a, aa] => 找当前模块的父模块
    const parent = path.slice(0, -1).reduce((start, current) => {
      return start[current]
    }, rootState);

    // 保证后续动态添加的模块，也具备响应式
    store._withCommiting(() => {
      // 是合法的修改，_withCommiting 忽略严格监控
      Vue.set(parent, path.at(-1), rootModule.state);
      // parent[path.at(-1)] = rootModule.state;
    })
  }

  // 我们需要获取到当前子模块是否开启了命名空间
  const namespaced = store._modules.getNamespaced(path);

  // 循环处理mutations 的所有属性
  rootModule.forEachMutation((mutationKey, mutationValue) => {
    // 初始化当前模块的_mutations 队列
    const key = namespaced + mutationKey;
    store._mutations[key] = (store._mutations[key] || []);

    // 将所有的mutations 里的方法维护到其队列中
    store._mutations[key].push((payload) => {
      // mutationValue(rootModule.state, payload);

      // 合法修改，更改状态的话肯定是在commiting 为true 的时候，状态的修改都需要是由mutation 去修改
      store._withCommiting(() => {
        // mutationValue 为用户定义的mutation 方法，传入当前模块的state 和 用户参数
        mutationValue(getState(store, path), payload);
      });

      // 每次状态更新，需要执行store.subscribes 内所有的订阅，将最新的state 传入
      store.subscribes.forEach(cb => cb({ type: mutationKey, payload }, store.state));
    });
  })

  // 循环处理actions 的所有属性
  rootModule.forEachAction((actionKey, actionValue) => {
    const key = namespaced + actionKey;
    store._actions[key] = (store._actions[key] || []);

    // 将所有的actions 里的方法维护到其队列中，同时返回用户函数的执行结果（处理Promise 等）
    store._actions[key].push((payload) => {
      // actionValue 为用户定义的action 方法，传入store 和 用户参数
      return actionValue(store, payload)
    });
  })

  // 循环处理getters 的所有属性，并包装成_wrappedGetters
  rootModule.forEachGetter((getterKey, getterValue) => {
    const key = namespaced + getterKey;
    if (store._wrappedGetters[key]) {
      return console.warn("key 重复")
    }

    // getterValue 为用户定义的getter 计算属性，传入当前模块的state
    store._wrappedGetters[key] = () => {
      return getterValue(getState(store, path))
    }
  })

  // 循环递归处理modules 的所有属性
  rootModule.forEachModule((moduleKey, module) => {
    installModule(store, rootState, path.concat(moduleKey), module);
  })
}

// 借助Vue 将store 挂载到实例上，并实现响应式
export function resetStoreVM(store, state) {
  // 存储一份老的_vm，便于卸载
  const oldVm = store._vm;
  // getters 计算属性
  store.getters = {};
  // 创建一个computed。挂载到创建的Vue 实例上，实现vuex 中getters 的响应式
  const computed = {};
  // 获取到包装后的getters
  const wrappedGetters = store._wrappedGetters;

  // 循环处理getters
  forEachValue(wrappedGetters, (getterKey, getterValue) => {
    // 将所有的getters 属性挂载到computed 上
    computed[getterKey] = getterValue;

    // 对store.getters 进行劫持，代理到_vm 的计算属性上，实现响应式
    Object.defineProperty(store.getters, getterKey, {
      get: () => {
        return store._vm[getterKey]
      }
    })
  })

  // 创建一个Vue 实例，借助Vue 的data 和computed 对state 和getters 进行响应式处理
  store._vm = new Vue({
    data: {
      $$state: state,
    },
    computed
  })

  // 如果开启了严格模式，借助Vue 的$watch 对非法修改state 的操作进行提醒
  if (store.strict) {
    store._vm.$watch(() => store._vm._data.$$state, () => {
      // store._commiting 为false 的时候，是非法操作
      console.assert(store._commiting, "非法修改，请通过commit 修改状态");
      // 同步监听且深度
    }, { sync: true, deep: true })
  }

  if (oldVm) {
    // 删除原有的创建的_vm
    Vue.nextTick(() => oldVm.$destroy());
  }
}

const install = (_vue) => {
  Vue = _vue;

  Vue.mixin({
    beforeCreate() {
      // 让所有的组件都定义一个$store 属性可以用来获取store 实例
      if (this.$options.store) {
        // 根组件
        this.$store = this.$options.store;
      } else if (this.$parent && this.$parent.$store) {
        // 子组件再通过父组件去获取$store
        this.$store = this.$parent.$store;
      }
    }
  })
}

export default install
